package net.facelib.sdk;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;


import gu.jimgutil.MatrixUtils;
import net.facelib.live.BaseFaceLive;
import net.facelib.live.FaceLive;
import net.facelib.live.LiveCodeInfo;
import net.facelib.live.LiveStatus;
import net.facelib.sdk.UserInfoManagement.User;
import net.gdface.image.ImageErrorException;
import net.gdface.image.MatType;
import net.gdface.sdk.BaseFaceApiLocal;
import net.gdface.sdk.CodeInfo;
import net.gdface.sdk.ContextLoader;
import net.gdface.sdk.NotFaceDetectedException;
import net.gdface.sdk.SDKLog;
import net.gdface.sdk.ContextLoader.GlobalContextField;
import net.gdface.sdk.fse.CodeBean;
import net.gdface.sdk.fse.FeatureSe;
import net.gdface.utils.ArrayUtils;
import net.gdface.utils.BinaryUtils;
import static net.facelib.sdk.FaceCaptureLog.CAPTURER_LOGGER;
import static net.gdface.utils.ConditionChecks.checkState;
import static net.gdface.utils.ConditionChecks.checkNotNull;
import static net.gdface.utils.ConditionChecks.checkTrue;
import static net.gdface.utils.ConditionChecks.checkArgument;
import static net.facelib.sdk.FaceCapturerSupport.getExecutor;

/**
 * 人脸视频检测封装<br>
 * @author guyadong
 *
 */
public abstract class FaceCapturer implements FaceCaptureHandler,CaptureConstants{
	protected boolean trace;
	/**
	 * 当前对象的工作状态:OPENED,CLOSED
	 */
	protected volatile int capturerStatus = CAPTURER_CLOSED;
	/**
	 * 线程局部初始化标志
	 */
	private ThreadLocal<Boolean> localInitialzied = new ThreadLocal<Boolean>(){

		@Override
		protected Boolean initialValue() {
			return false;
		}		
	};
	/**
	 * FaceApi本地实例
	 */
	private BaseFaceApiLocal faceApiLocal;
	/**
	 * 活体检测实例
	 */
	private FaceLive facelive;	
	/**
	 * 是否检测活体
	 */
	private boolean detectLive = false;
	/**
	 * 是否检测最大人脸
	 */
	private boolean detectMaxFace = true;
	/**
	 * 是否提取人脸特征
	 */
	private boolean extractFeature;

	/**
	 * 人脸识别模式:不识别,1:1识别,1:N识别
	 */
	private int recongizeMode;

	/**
	 * 活体检测要求旋转矩阵的角度:0,90,180,270
	 */
	private int liveRotate = 0;
	/**
	 * 人脸检测,提取特征要求旋转矩阵的角度:0,90,180,270
	 */
	private int detectRotate;
	/**
	 * 图像矩阵类型
	 */
	private MatType matType;
	/**
	 * 图像矩阵宽度
	 */
	private int capWidth;
	/**
	 * 图像矩阵高度
	 */
	private int capHeight;
	/**
	 * 视频检测回调接口实例
	 */
	private FaceCaptureCallback callback;
	/**
	 * 1:N特征搜索引擎实例
	 */
	private FeatureSe fse;
	/**
	 * 人脸特性比对相似度阀值
	 */
	private double similarityThreshold;
	/**
	 * 1:N比对返回的最大结果数量
	 */
	private int fseMaxRows;
	/**
	 * 用户信息存储管理对象
	 */
	private UserInfoManagement userInfoManagement;
	/**
	 * 1:1搜索时用于比较的人脸特征
	 */
	private CodeInfo oneUserCodeInfo;
	
	/**
	 * 当执行关闭时是否释放算法模块占用的资源
	 */
	private boolean releaseSdkModulesOnClose = false;
	protected FaceCapturer() {
		super();
		this.faceApiLocal = checkNotNull(getFaceapi(),"getFaceapi() return null");
		this.fse = faceApiLocal.getFeatureSe();
		this.similarityThreshold = getDefaultSimilarityThreshold();
		this.fseMaxRows = CaptureConstants.DEFAULT_FSE_MAX_ROWS;
		this.facelive = getFacelive();
		this.callback = new FaceCaptureLogCallback(true);
		this.userInfoManagement = new UserInfoManagement();
	}
	protected BaseFaceApiLocal getFaceapi(){
		return null;
	}

	protected FaceCaptureCallback getCallback() {
		return callback;
	}
	protected FaceLive getFacelive() {
		return BaseFaceLive.getInstanceUnchecked();
	}
	/**
	 * @return 返回默认的特征比对相似度阀值
	 */
	protected double getDefaultSimilarityThreshold(){
		return CaptureConstants.DEFAULT_SIMILARITYTHRESHOLD;
	}
	protected void doLocalInitOnce(){
		
	}
	protected void doLocalUninit(){
		
	}
	protected void doInit(){
		
	}
	protected void doUnInit(){
		
	}
	/**
	 * 释放算法模块占用的资源
	 */
	protected void doReleaseSdkModules(){
		// DO NOTHING
	}
	/**
	 * 异步执行{@link #doReleaseSdkModules()}
	 */
	private void releaseSdkModulesAsync(){
		if(releaseSdkModulesOnClose){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					CAPTURER_LOGGER.log("releaseSdkModulesAsync");
					doReleaseSdkModules();
					
				}
			},"release-sdk-modules").start();
		}
	}
	/**
	 * 矩阵数据压缩为JPEG
	 * @param matType 矩阵类型
	 * @param matData 图像矩阵
	 * @param width 矩阵宽度
	 * @param height 矩阵高度
	 * @return JPEG格式图像数据
	 */
	protected byte[] matToJpeg(MatType matType, byte[] matData, int width, int height){
		return null;
	}
	private static CaptureContext rotate(MatType matType, byte[] matData, int width, int height, int rotate){
		byte[] rotatedMat;
		int ow,oh;
		if(0 == rotate){
			ow = width;
			oh = height;
			rotatedMat = matData;
		}else{
			switch (rotate) {
			case 90:
			case 270:
				ow = height;
				oh = width;
				break;
			case 180:
				ow = width;
				oh = height;
				break;
			default:
				throw new UnsupportedOperationException("UNSUPPORTED rotate Angle: " + rotate + ", [0,90,180,270] required");
			}
			switch (matType) {
			case NV21:
			{
				rotatedMat = matData.clone();
				MatrixUtils.nv21Rotate(rotatedMat, width, height, rotate);
				break;
			}
			case RGB:
			case BGR:{
				rotatedMat = matData.clone();
				MatrixUtils.rotate(rotatedMat, width, height, 3, rotate);
				break;
			}			
			case RGBA:{
				rotatedMat = matData.clone();
				MatrixUtils.rotate(rotatedMat, width, height, 4, rotate);
				break;
			}
			case GRAY:{
				rotatedMat = matData.clone();
				MatrixUtils.rotate(rotatedMat, width, height, 1, rotate);
				break;
			}
			default:
				throw new UnsupportedOperationException("UNSUPPORTED MATRIX TYPE: " + matType);
			}
		}
		return new CaptureContext(matType, rotatedMat, ow, oh);
	}
	/**
	 * 执行人脸检测
	 * @param context
	 * @return 人脸信息对象数组
	 * @throws NotFaceDetectedException
	 */
	protected CodeInfo[] detectFace(CaptureContext context) throws NotFaceDetectedException{
		if(detectMaxFace){			
			return new CodeInfo[]{faceApiLocal.matDetectMaxFace(context.matType, context.matData, context.width, context.height)};
		}else{			
			 return faceApiLocal.matDetectFace(context.matType, context.matData, context.width, context.height, 0);
		}
	}
	/**
	 * 特征提取及特征比对
	 * @param context
	 * @param codes
	 */
	protected void extractFeatureAndRecongize(CaptureContext context,CodeInfo[] codes){

		if(null == context || null == codes || codes.length == 0){
			return ;
		}
		try {
			faceApiLocal.matGetCodeInfo(context.matType, context.matData, context.width, context.height, codes.length,codes);
			callback.onFaceFeature(context, codes);
		} catch (Throwable e) {
			callback.onFeatureError(context, codes, e);
			return;
		}
		try {
			switch (recongizeMode) {
			case CaptureFeatures.RECOGNIZE_ONE:
				/** 1:1 比对*/
				if(null == oneUserCodeInfo || null == oneUserCodeInfo.getCode()){
					throw new IllegalStateException("INVALID oneUserCodeInfo,must call registerFace firstly");
				}
				for(CodeInfo code:codes){
					double sim = faceApiLocal.compareCode(oneUserCodeInfo.getCode(), code.getCode());
					if(sim > similarityThreshold){
						Collection<User> users = userInfoManagement.getAllUsers();
						if(users.isEmpty()){
							// 用户数据为空
							CAPTURER_LOGGER.log("NOT DEFINED User information for 1:1 recognition");
						}else {
							if(users.size() > 1){
								// 用户数据超过1个报警
								CAPTURER_LOGGER.log("More than one user found for 1:1 recognition");
							}								
							Iterator<User> itor = userInfoManagement.getAllUsers().iterator();
							callback.onRecongizeOne(context, code, itor.next(),sim);							
						}
						break;
					}						
				}
				break;
			case CaptureFeatures.RECOGNIZE_N:
				checkTrue(null != fse, UnsupportedOperationException.class, "the fse (FeatureSe instance) is null");
				/** 1:N 比对*/
				List<CodeInfo> recognized = new ArrayList<>(codes.length);
				for(CodeInfo featureCode:codes){								
					CodeBean[] codebeans = fse.searchCode(featureCode.getCode(), similarityThreshold, fseMaxRows, null, 0);
					if(codebeans.length != 0){
						User[] userIDs = new User[codebeans.length];
						double[] sims = new double[codebeans.length];
						for(int j=0;j<codebeans.length;++j){										
							userIDs[j] = userInfoManagement.getUser(BinaryUtils.toHex(codebeans[j].id));
							sims[j] = codebeans[j].similarity;
							recognized.add(featureCode);
						}
						callback.onRecongizeN(context, recognized.toArray(new CodeInfo[recognized.size()]), userIDs, 
								sims);
					}
				}
				break;
			case CaptureFeatures.RECOGNIZE_NONE:
				break;
			default:
				throw new IllegalArgumentException("INVALID recongizeMode: " + recongizeMode);
			}
		} catch (Throwable e) {
			callback.onRecongizeError(context, codes, e);
			return;
		}
	}
	/** 异步执行提取特征和比对 */
	protected void extractFeatureAndRecongizeAsync(final CaptureContext detectContext,final CodeInfo[] codes){
		getExecutor().execute(new Runnable() {

			@Override
			public void run() {
				try{
					if(checkContinue(false)){
						extractFeatureAndRecongize(detectContext,codes);
					}
				}catch (Throwable e) {
					doOnUndeclaredCaptureException(detectContext,e);
				} finally{
					localUninitIfClosed(false);
				}
			}
		});
	}
	/**
	 * 异步执行活体检测
	 * @param context
	 * @param codes
	 */
	protected void liveProcessAsync(final CaptureContext context,final CodeInfo[] codes){
		getExecutor().execute(new Runnable() {
			@Override
			public void run() {
				try{
					if(!checkContinue(false)){
						return ;
					}
					// 下面这个判断逻辑必须放在 checkContinue 后面
					if(null == context || null == codes || codes.length == 0){
						return ;
					}
					// 根据活体检测状态过滤所有判断为活体或非活体的CodeInfo对象提取特征
					ArrayList<LiveCodeInfo> positiveCodes = new ArrayList<>(codes.length);
					try{
						checkState(null != facelive,"FaceLive instance is null");
						for(CodeInfo code:codes){
							AtomicReference<Double> score = new AtomicReference<Double>(0D);
							LiveStatus result = facelive.matLiveProcess(context.matType, context.matData, context.width, context.height, code.getPos(), score);							
							switch(result){
							case LIVE_ERROR:
								callback.onLiveFaceError(context, result);
								break;
							case NOT_LIVE:
							case LIVE:
								// 只保留判断为活体或非活体的人脸位置
								positiveCodes.add(new LiveCodeInfo(code,result,score.get()));
								break;
							default:
								// 忽略UNKNOW
								break;
							}
						}
						if(positiveCodes.isEmpty()){
							return;
						}
						callback.onLiveFaces(context, positiveCodes.toArray(new LiveCodeInfo[positiveCodes.size()]));	
					} catch (Throwable e) {
						callback.onLiveFaceError(context, e);
						return ;
					}

					if(extractFeature || recongizeMode != CaptureFeatures.RECOGNIZE_NONE){
						// 根据活体检测状态过滤所有为活体的CodeInfo对象提取特征
						List<LiveCodeInfo> liveCodes = new ArrayList<>(positiveCodes.size());
						for(LiveCodeInfo positiveCode:positiveCodes){
							if(LiveStatus.LIVE.equals(positiveCode.getLiveStatus())){
								liveCodes.add(positiveCode);
							}
						}
						extractFeatureAndRecongize(context, liveCodes.toArray(new LiveCodeInfo[liveCodes.size()]));
					}
				} catch (Throwable e) {
					doOnUndeclaredCaptureException(context,e);
				} finally{
					localUninitIfClosed(false);
				}
			}
		});	
	}
	protected void capture(MatType matType, byte[] matData, int width, int height, int liveRotate,int detectRotate) {
		try{
			if(!checkContinue(true)){
				return ;
			}
			CaptureContext detectContext = rotate(matType,matData,width,height,detectRotate);
			CodeInfo[] codes;
			try {
				codes = detectFace(detectContext);
				CodeInfo[] filtered = callback.onFaceDetected(detectContext, codes);
				// onFaceDetected返回的数组为null或长度为0视为没检测到人脸抛出异常
				checkTrue(null != filtered && 0 != filtered.length, NotFaceDetectedException.class,"not matched face detected");
				// 过滤 filtered 所有的为空的元素
				checkState(filtered.length <= codes.length, "INVALID filter codes lengh,must <= codes");
				codes = ArrayUtils.cleanNull(filtered);
				// 过滤后的数组长度为0视为没检测到人脸抛出异常
				checkTrue(0 != codes.length, NotFaceDetectedException.class,"not matched face detected");
			}catch (NotFaceDetectedException e) {
				callback.onNotFaceDetectedException(detectContext);
				return;
			}
			if(detectLive){
				// liveRotate == detectRotate 则使用 detectContext,否则创建新的 CaptureContext 实例 
				CaptureContext liveContext = liveRotate == detectRotate ? detectContext : rotate(matType,matData,width,height,liveRotate);
				liveProcessAsync(liveContext, codes);	
			}else	if(extractFeature || recongizeMode != CaptureFeatures.RECOGNIZE_NONE){				
				extractFeatureAndRecongizeAsync(detectContext,codes);
			} 
		}catch (Throwable e) {
			doOnUndeclaredCaptureException(new CaptureContext(matType, matData, width, height),e);
		} finally{
			// 关闭状态执行当前线程局部变量
			localUninitIfClosed(true);
			// 关闭状态强制异步执行 localUninitIfClosed,清理 face-capturer 线程局部变量
			if(isClosed()){
				liveProcessAsync(null, null);
			}
		}
	}
	@Override
	public void capture(byte[] matData){
		checkNotNull(matType, "matType is null");
		checkArgument(capWidth > 0 , "INVALID capWidth,%s", capWidth);
		checkArgument(capHeight > 0 , "INVALID capHeight %s", capHeight);
		capture(matType,matData,capWidth,capHeight, liveRotate,detectRotate);
	}
	
	/**
	 * 调用{@link FaceCaptureCallback#onUndeclaredCaptureException(CaptureContext, Throwable)}方法处理未预期异常,
	 * 如果调用时抛出异常则日志输出
	 * @param context
	 * @param e undeclared exception
	 */
	protected void doOnUndeclaredCaptureException(CaptureContext context,Throwable e){
		try {
			callback.onUndeclaredCaptureException(context,e);
		} catch (Throwable x) {
			CAPTURER_LOGGER.log("{}:{}",x.getClass().getName(),x.getMessage());
			CAPTURER_LOGGER.logTrace(trace, x);
		}
	}
	/**
	 * 调用{@link FaceCaptureCallback#onUndeclaredException(Throwable)}方法处理未预期异常,
	 * 如果调用时抛出异常则日志输出
	 * @param e undeclared exception
	 */
	protected void doOnUndeclaredException(Throwable e){
		try {
			callback.onUndeclaredException(e);
		} catch (Throwable x) {
			CAPTURER_LOGGER.log("{}:{}",x.getClass().getName(),x.getMessage());
			CAPTURER_LOGGER.logTrace(trace, x);
		}
	}
	/**
	 * 根据当前捕获实例的状态{@link #capturerStatus}返回是否要继续后续的操作<br>
	 * 并执行线程局部初始化或状态复位
	 * @param forDetect 当前线程是否为检测线程
	 * @return 如果{@link #capturerStatus}为{@link CaptureConstants#CAPTURER_OPENED}返回{@code true},否则返回{@code false}
	 */
	private boolean checkContinue(boolean forDetect){
		if(localInitOnceIfOpened(forDetect)){
			return true;
		}
		return localUninitIfClosed(forDetect);
	}
	/**
	 * 线程局部初始化<br>
	 * {@link #capturerStatus}状态为关闭时忽略
	 * @param forDetect 当前线程是否为检测线程
	 * @return 如果{@link #capturerStatus}为{@link CaptureConstants#CAPTURER_OPENED}返回{@code true},否则返回{@code false}
	 */
	private boolean localInitOnceIfOpened(boolean forDetect){
		if(capturerStatus == CAPTURER_OPENED){
			if(!Boolean.TRUE.equals(localInitialzied.get())){
				faceApiLocal.reset();
				if(forDetect){
					ContextLoader.setLocalContext(GlobalContextField.MULIT_FACE_DETECT_TREND, false);
					CAPTURER_LOGGER.log("bindNativeDetectInstanceForCurentThread");
					faceApiLocal.bindNativeDetectInstanceForCurentThread();
				}else{
					if(extractFeature){
						CAPTURER_LOGGER.log("bindNativeFeatureInstanceForCurentThread");
						faceApiLocal.bindNativeFeatureInstanceForCurentThread();
					}
					if(detectLive && (facelive instanceof BaseFaceLive)){
						CAPTURER_LOGGER.log("bindNativeInstanceForCurentThread");
						((BaseFaceLive)facelive).bindNativeInstanceForCurentThread();
					}
				}
				doLocalInitOnce();
				localInitialzied.set(true);
			}
			return true;
		}
		return false;
	}
	/**
	 * 线程局部状态复位<br>
	 * 如果{@link #capturerStatus}为{@link CaptureConstants#CAPTURER_CLOSED}则执行线程局部状态复位,
 	 * {@link #capturerStatus}状态为开启时忽略
	 * @param forDetect 当前线程是否为检测线程
	 * @return always false
	 */
	private boolean localUninitIfClosed(boolean forDetect) {
		if(capturerStatus == CAPTURER_CLOSED){
			if(Boolean.TRUE.equals(localInitialzied.get())){
				doLocalUninit();
				if(forDetect){
					CAPTURER_LOGGER.log("unbindNativeDetectInstanceForCurentThread");
					faceApiLocal.unbindNativeDetectInstanceForCurentThread();
				}else{
					if(extractFeature){
						CAPTURER_LOGGER.log("unbindNativeFeatureInstanceForCurentThread");
						faceApiLocal.unbindNativeFeatureInstanceForCurentThread();
					}
					if(detectLive &&  (facelive instanceof BaseFaceLive)){
						((BaseFaceLive)facelive).unbindNativeInstanceForCurentThread();
					}
				}
				localInitialzied.remove();
			}
		}
		return false;
	}
	/**
	 * 根据提供的参数初始化并开始视频检测
	 * @param matType 图像矩阵类型
	 * @param width 矩阵宽度
	 * @param height 矩阵高度
	 * @param fseMaxRows 1:N搜索最大返回记录数量
	 * @param similarityThreshold 人脸特征对比相似度阀值
	 * @param userInfoManagement 用户信息管理实例
	 * @param callback 视频检测回调接口实例
	 * @param facelive 活体检测实例
	 * @param features 视频检测特性参数,参见{@link CaptureFeatures}
	 */
	public void open(MatType matType, int width, int height,
			int fseMaxRows, double similarityThreshold,
			UserInfoManagement userInfoManagement,
			FaceCaptureCallback callback,
			FaceLive facelive,
			int ...features){
		setCaptureMat(matType, width, height);
		setRecognitionParam(fseMaxRows, similarityThreshold);
		setUserInfoManagement(userInfoManagement);
		setCallback(callback);
		setFacelive(facelive);
		setFeature(features);
		open();
	}
	@Override
	public void open(){
		synchronized (this) {
			if(capturerStatus == CAPTURER_CLOSED){
				try{
					doInit();
					capturerStatus = CAPTURER_OPENED;
					CAPTURER_LOGGER.log("capturerStatus {}",capturerStatus);					
				} catch (Throwable e) {
					doOnUndeclaredException(e);
				}
			}	
		}
	}
	@Override
	public void close() {
		synchronized (this) {
			if(capturerStatus == CAPTURER_OPENED){
				try {
					doUnInit();
					releaseSdkModulesAsync();
					capturerStatus = CAPTURER_CLOSED;
					CAPTURER_LOGGER.log("capturerStatus {}",capturerStatus);
				} catch (Throwable e) {
					doOnUndeclaredException(e);
				} 
			}
		}
	}
	
	public boolean isClosed(){
		return capturerStatus == CAPTURER_CLOSED;
	}
	/**
	 * 开启调试日志输出
	 * @return 当前对象
	 */
	public FaceCapturer enableDebugLog(){
		/** 开启SDK层日志输出 */
		SDKLog.SDK_LOGGER.enableConsoleLog();
		return this;
	}
	
	/**
	 * 设置当执行关闭时是否释放算法模块占用的资源,默认{@code false},
	 * 对于配置较低,内存资源紧张的设备,可以设置为{@code true}以减少内存资源占用
	 * @param releaseSdkModulesOnClose
	 * @return 当前对象
	 */
	public FaceCapturer setReleaseSdkModulesOnClose(boolean releaseSdkModulesOnClose) {
		this.releaseSdkModulesOnClose = releaseSdkModulesOnClose;
		return this;
	}
	/**
	 * 设置当执行关闭时是否释放算法模块占用的资源,默认{@code false}
	 * @param trace
	 * @return 当前对象
	 */
	public FaceCapturer setTrace(boolean trace) {
		this.trace = trace;
		return this;
	}
	/**
	 * 设置视频检测回调接口
	 * @param callback
	 * @return 当前对象
	 */
	public FaceCapturer setCallback(FaceCaptureCallback callback) {
		if(callback != null){
			this.callback = callback;
		}
		return this;
	}
	
	/**
	 * 设置活体检测实例
	 * @param facelive
	 * @return 当前对象
	 */
	public FaceCapturer setFacelive(FaceLive facelive) {
		this.facelive = facelive;
		return this;
	}
	
	/**
	 * 设置人脸识别算法实例
	 * @param faceapi
	 * @return 当前对象
	 */
	public FaceCapturer setFaceapi(BaseFaceApiLocal faceapi) {
		this.faceApiLocal = checkNotNull(faceapi,"faceapi is null");
		return this;
	}
	
	/**
	 * 设置视频检测特性参数,参见{@link CaptureFeatures}
	 * @param features
	 * @return 当前对象
	 */
	public FaceCapturer setFeature(int ...features){
		int fea = 0 ;
		for(int feature : features){
			fea |= feature;
		}
		detectLive = CaptureFeatures.isDetectMaxFace(fea);
		detectMaxFace = CaptureFeatures.isDetectMaxFace(fea);
		liveRotate = CaptureFeatures.getLiveRotate(fea);
		detectRotate = CaptureFeatures.getDetectRotate(fea);
		extractFeature = CaptureFeatures.isExtractFeature(fea);
		recongizeMode = CaptureFeatures.getRecognize(fea);
		return this;
	}
	
	/**
	 * 设置视频图像矩阵参数
	 * @param matType
	 * @param width
	 * @param height
	 * @return 当前对象
	 */
	public FaceCapturer setCaptureMat(MatType matType, int width, int height){
		this.matType = matType;
		this.capWidth = width;
		this.capHeight = height;
		return this;
	}
	/**
	 * 设置人脸识别参数
	 * @param fseMaxRows 1:N搜索最大返回记录数量,小于等于0忽略 
	 * @param similarityThreshold 人脸特征对比相似度阀值,有效值范围(0~1),超出有效值范围忽略
	 * @return 当前对象
	 */
	public FaceCapturer setRecognitionParam(int fseMaxRows, double similarityThreshold){
		if(fseMaxRows > 0){
			this.fseMaxRows = fseMaxRows;
		}
		if(similarityThreshold > 0 && similarityThreshold <= 1){
			this.similarityThreshold = similarityThreshold;
		}
		return this;
	}
	/**
	 * 设置用户信息管理实例
	 * @param userInfoManagement 为{@code null}忽略
	 * @return 当前对象
	 */
	public FaceCapturer setUserInfoManagement(UserInfoManagement userInfoManagement) {
		if(null != userInfoManagement){
			this.userInfoManagement = userInfoManagement;
		}
		return this;
	}
	/**
	 * 提取人脸特征注册人脸数据
	 * @param user 用户数据
	 * @return 当前对象
	 * @throws ImageErrorException 图像数据错误
	 * @throws NotFaceDetectedException 没有检测到人脸或无法提取特征
	 * @throws IOException 
	 */
	void registerFace(User user) throws ImageErrorException, NotFaceDetectedException, IOException{
		if(null != user && null != user.getUserID() && null != user.getPhoto()){
			byte[] imgData = BinaryUtils.getBytesNotEmpty(user.getPhoto());
			String imageMD5 = BinaryUtils.getMD5String(imgData);
			
			CodeInfo codeInfo = faceApiLocal.detectMaxFaceAndGetCodeInfo(imgData);
			byte[] featureMD5 = BinaryUtils.getMD5(codeInfo.getCode());
			String featureId = BinaryUtils.toHex(featureMD5);
			userInfoManagement.updateUser(user.getUserID(),featureId);
			if(null != fse){
				fse.addFeature(featureMD5, codeInfo.getCode(), imageMD5, 0);			
			}
			if(recongizeMode == CaptureFeatures.RECOGNIZE_ONE){				
				this.oneUserCodeInfo = codeInfo;
			}
			CAPTURER_LOGGER.log("register face {}", user);
		}
	}
	/**
	 * 对{@link UserInfoManagement}中的注册的所有用户注册人脸信息
	 * @return 成功注册的用户数量
	 */
	protected synchronized int doRegisterAllFaces(){
		if(null != fse){
			fse.clearAll();
		}
		int registerCount = 0;
		for(User user : userInfoManagement.getAllUsers()){
			try{
				registerFace(user);
				registerCount ++;
			} catch (ImageErrorException | NotFaceDetectedException | IOException e) {
				CAPTURER_LOGGER.log("{}:{}",e.getClass().getName(),e.getMessage());
				CAPTURER_LOGGER.logTrace(trace, e);
			}
		}
		callback.onUserFaceRegistered(userInfoManagement);
		return registerCount;
	}
	/**
	 * 对{@link UserInfoManagement}中的注册的所有用户注册人脸信息
	 * @param syncLock 同步信号对象,不为{@code null}时异步执行,当人脸注册执行完成后会唤醒阻塞此信号量的所有线程,{@code syncLock}的值为注册成功的用户数量
	 * 							 为{@code null}时同步执行
	 * @return 同步执行时返回注册成功的用户数量,异步执行时返回 0
	 * 
	 */
	public int registerAllFaces(final AtomicInteger syncLock){
		if(null != syncLock){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					int count = doRegisterAllFaces();
					syncLock.set(count);
					synchronized (syncLock) {
						syncLock.notifyAll();
					}
				}
			},"register-face").start();
			return 0;
		}else{
			return doRegisterAllFaces();
		}
	}
	/**
	 * 注册用户数据
	 * @param userID 用户名
	 * @param photo 用户照片
	 * @param description 用户描述可为{@code null}
	 * @return 当前对象
	 */
	public FaceCapturer registerUser(String userID, URL photo, String description) {
		if(recongizeMode == CaptureFeatures.RECOGNIZE_ONE){
			userInfoManagement.clear();
		}
		userInfoManagement.addUserInfo(userID, photo, description);
		return this;
	}
	/**
	 * 注册用户数据
	 * @param name 用户名
	 * @param imgFile 图像文件(jpg,png...)
	 * @param description 用户描述可为{@code null}
	 * @return 当前对象
	 */
	public FaceCapturer registerUser(String  name,File imgFile,String description){
		try {
			return registerUser(name, imgFile == null ? null : imgFile.toURI().toURL(), description);
		} catch (MalformedURLException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 返回 {@link FaceCapturer} 实例,实例为{@code null}抛出异常
	 * @return FaceCapturer instance
	 */
	public static FaceCapturer getInstance() {
		if(null == FaceCapturerSupport.instance){
			throw new NullPointerException("default CaptureHandler instance is null");
		}
		return FaceCapturerSupport.instance;
	}
	/**
	 * 返回 {@link FaceCapturer} 实例,如果没有找到实例则返回{@code null}
	 * @return FaceCapturer instance
	 */
	public static FaceCapturer getInstanceUnchecked() {
		return FaceCapturerSupport.instance;
	}
	public static void setInstance(FaceCapturer capturerHandler){
		FaceCapturerSupport.instance = capturerHandler;
	}
	/**
	 * 返回指定产品ID的实例
	 * @param productID 产品ID
	 * @return FaceCaptureHandler instance
	 */
	public static FaceCaptureHandler getInstance(String productID) {		
		return null == productID ? null : FaceCapturerSupport.captureInstances.get(productID);
	}
	
	/**
	 * 设置指定产品ID的实例
	 * @param productID 产品ID
	 * @param instance FaceLive instance
	 */
	public synchronized static void setInstance(String productID,FaceCaptureHandler instance) {
		if(null == productID && null != instance){
			FaceCapturerSupport.captureInstances.put(productID,instance);
		}
	}

	

}
